﻿using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

using RiskCalc.Modelo;

namespace RiskCalc.Persistencia
{
    public class Persistencia
    {
        public static string CONST_NOME_ARQUIVO_XLSX = "DadosAcoes.xlsx";
        public static string CONST_NOME_PLANILHA_GERADA = "DadosGerados";

        public static bool carregarDadosDoBanco(Carteira carteira, out string msgRetorno, int janelaDias)
        {
            //// Acessa o root do projeto, pois o método Open() a seguir não aceita path relativo
            string pathArquivoExcel = Environment.CurrentDirectory.Replace("bin\\Debug", "");

            // Se não funcionar a troca, tenta denovo com modo "Release"
            if (pathArquivoExcel.Length.Equals(Environment.CurrentDirectory.Length))
            {
                pathArquivoExcel = Environment.CurrentDirectory.Replace("bin\\Release", "");
            }

            // RELEASE
            //string pathArquivoExcel = Environment.CurrentDirectory + @"\";

            // Se não funcionar novamente, é pq está no mesmo path do exe, logo não precisa trocar
            OleDbConnection connection = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + pathArquivoExcel + CONST_NOME_ARQUIVO_XLSX + ";Extended Properties=&quot;Excel 12.0 Xml;HDR=YES&quot;");

            OleDbCommand command1 = new OleDbCommand("SELECT * FROM [Valores$] ORDER BY ACAO, DATA DESC;", connection);
            OleDbCommand command2 = new OleDbCommand("SELECT * FROM [Quantidades$];", connection);

            connection.Open();

            ////////////////////////////////////////////////////////////////////////////////////
            // Obtém as ações e seus dados
            ////////////////////////////////////////////////////////////////////////////////////
            OleDbDataReader reader1 = command1.ExecuteReader();

            carteira.vetAcao = new List<Acao>();
            Dado dado = null;
            Acao acao = null;

            while (reader1.Read())
            {
                if (acao == null || acao.Nome != reader1.GetString(1))
                {
                    acao = new AcaoSimulacao();
                    acao.Nome = reader1.GetString(1);
                    carteira.vetAcao.Add(acao);
                }

                dado = new DadoSimulacao();
                dado.Acao = acao;
                dado.Valor = reader1.GetDouble(2);
                dado.Dia = reader1.GetDateTime(0);
                acao.VetDados.Add(dado);
            }

            foreach (Acao acaoAux in carteira.vetAcao)
            {
                acaoAux.VetDados = acaoAux.VetDados.GetRange(0, janelaDias + 1);
            }

            reader1.Close();


            ////////////////////////////////////////////////////////////////////////////////////
            // Obtém as quantidades
            ////////////////////////////////////////////////////////////////////////////////////
            OleDbDataReader reader2 = command2.ExecuteReader();

            while (reader2.Read())
            {
                acao = carteira.vetAcao.Find(acaoAux => acaoAux.Nome.Equals(reader2.GetString(0)));
                acao.Quantidade = Convert.ToInt32(reader2.GetDouble(1));
            }

            reader2.Close();

            msgRetorno = "Sucesso";
            return true;
        }

        public static bool imprimirNoExcel(Carteira carteira, out string msgRetorno, bool imprimirDebug)
        {
            ApplicationClass excelApp = new ApplicationClass();
            Workbook objBook;
            Worksheet objSheet;

            object missing = System.Reflection.Missing.Value;

            excelApp.Visible = true;
            excelApp.UserControl = true;
            msgRetorno = "";

            // (RELEASE) Acessa o root do projeto, pois o método Open() a seguir não aceita path relativo
            //string pathArquivoExcel = Environment.CurrentDirectory + @"\";

            //// Acessa o root do projeto, pois o método Open() a seguir não aceita path relativo
            string pathArquivoExcel = Environment.CurrentDirectory.Replace("bin\\Debug", "");

            // Se não funcionar a troca, tenta denovo com modo "Release"
            if (pathArquivoExcel.Length.Equals(Environment.CurrentDirectory.Length))
            {
                pathArquivoExcel = Environment.CurrentDirectory.Replace("bin\\Release", "");
            }

            // Se não funcionar a troca, o arquivo Excel está no mesmo diretório que o executável, logo apenas adiciona "\"
            if (pathArquivoExcel.Length.Equals(Environment.CurrentDirectory.Length))
            {
                pathArquivoExcel += @"\";
            }

            // Tenta abrir o arquivo
            try
            {
                objBook = excelApp.Workbooks.Open(pathArquivoExcel + CONST_NOME_ARQUIVO_XLSX,
                    missing, missing, missing, missing, missing, missing, missing,
                    missing, missing, missing, missing, missing, missing, missing);
            }
            catch (COMException)
            {
                msgRetorno = "Arquivo " + CONST_NOME_ARQUIVO_XLSX + " não encontrado. Ele deve estar na raiz do projeto.";
                objBook = null;
                return false;
            }

            // Cria nova planilha como "Plan1"
            objSheet = (Worksheet)objBook.Worksheets.Add(missing, missing, missing, missing);

            msgRetorno = "";

            // Gera o novo nome para "Plan1"
            string nomeUltimaPlanilha = ((Worksheet)excelApp.Worksheets[3]).Name;
            int ultimoNumeroPlanilha = 1;

            // Tenta renomear a planilha
            try
            {
                // Se não for a primeira vez
                if (!nomeUltimaPlanilha.Equals("Valores") && !nomeUltimaPlanilha.Equals("Quantidades") && !nomeUltimaPlanilha.Equals("Plan1"))
                {
                    // Obtém o último numero
                    ultimoNumeroPlanilha = Convert.ToInt16(nomeUltimaPlanilha.Substring(CONST_NOME_PLANILHA_GERADA.Length, (nomeUltimaPlanilha.Length - CONST_NOME_PLANILHA_GERADA.Length)));

                    // Gera com o próximo número "ComparativoN"
                    objSheet.Name = CONST_NOME_PLANILHA_GERADA + (ultimoNumeroPlanilha + 1);
                }
                else
                {
                    // Se for a primeira vez
                    objSheet.Name = CONST_NOME_PLANILHA_GERADA + ultimoNumeroPlanilha;
                }
            }
            catch (COMException)
            {
                msgRetorno = "Erro na numeração da nova planilha gerada.";
                return false;
            }

            //gerarNovaWorksheet(excelApp, objBook, carteira, missing, out objSheet, out msgRetorno);

            try
            {
                imprimirResultadoVaRsCarteira(objSheet, carteira);

                imprimirResultadoVaRsPorAcao(objSheet, carteira);

                imprimirMatrizCovariancia(objSheet, carteira);

                imprimirMatrizDecomposicaoCholesky(objSheet, carteira);

                imprimirMatrizCorrelacao(objSheet, carteira);

                imprimirDadosAcoes(objSheet, carteira);

                msgRetorno = "Sucesso";
                return true;
            }
            catch (COMException)
            {
                msgRetorno = "Geração de Relatório no Excel interrompida por clique do usuário.";
                return false;
            }
        }

        //private static bool abrirArquivo(ApplicationClass excelApp, Object missing, out Workbook objBook, out string msgRetorno)
        //{


        //    return true;
        //}

        //private static bool gerarNovaWorksheet(ApplicationClass excelApp, Workbook objBook, Carteira carteira, Object missing, out Worksheet objSheet, out string msgRetorno)
        //{
           

        //    return true;
        //}

        private static void imprimirResultadoVaRsCarteira(Worksheet objSheet, Carteira carteira)
        {
            // Define formatação das colunas
            objSheet.get_Range("B23", "B25").Font.Bold = true;
            objSheet.get_Range("A23", "D25").BorderAround(XlLineStyle.xlContinuous,
                       XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic,
                       XlColorIndex.xlColorIndexAutomatic);

            objSheet.get_Range("D23", "D25").NumberFormat = "R$ #.##0,00_);[Vermelho](R$ #.##0,00)";

            // Preenche os cabeçalhos
            objSheet.Cells[23, 2] = "VaR Paramétrico da Carteira";
            objSheet.Cells[23, 4] = carteira.varParametrico;

            objSheet.Cells[24, 2] = "VaR Histórico da Carteira:";
            objSheet.Cells[24, 4] = ((CarteiraSimulacao)carteira).VarHistorico;

            objSheet.Cells[25, 2] = "VaR Sim. de Monte Carlo da Carteira";
            objSheet.Cells[25, 4] = ((CarteiraSimulacao)carteira).VarMonteCarlo;
        }

        private static void imprimirResultadoVaRsPorAcao(Worksheet objSheet, Carteira carteira)
        {
            // Define formatação das colunas
            objSheet.get_Range("A1", "AB1").Font.Bold = true;
            objSheet.get_Range("A1", "A20").Font.Bold = true;
            objSheet.get_Range("A1", "G1").EntireColumn.HorizontalAlignment = XlVAlign.xlVAlignCenter;
            objSheet.get_Range("A1", "G1").BorderAround(XlLineStyle.xlContinuous,
                       XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic,
                       XlColorIndex.xlColorIndexAutomatic);
            objSheet.get_Range("A1", "G20").BorderAround(XlLineStyle.xlContinuous,
           XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic,
           XlColorIndex.xlColorIndexAutomatic);

            // Preenche os cabeçalhos
            objSheet.Cells[1, 1] = "Ação";
            objSheet.get_Range("A1", "A1").EntireColumn.ColumnWidth = 9.57;

            objSheet.Cells[1, 2] = "Quantidade";
            objSheet.get_Range("B1", "B1").EntireColumn.ColumnWidth = 13;
            objSheet.get_Range("B1", "B1").EntireColumn.NumberFormat = "#.##0";

            objSheet.Cells[1, 3] = "Posição";
            objSheet.get_Range("C1", "C1").EntireColumn.ColumnWidth = 15;
            objSheet.get_Range("C1", "C1").EntireColumn.NumberFormat = "R$ #.##0,00_);[Vermelho](R$ #.##0,00)";

            objSheet.Cells[1, 4] = "Média Retornos";
            objSheet.get_Range("D1", "D1").EntireColumn.ColumnWidth = 15;
            objSheet.get_Range("D1", "D20").NumberFormat = "0,000%";

            objSheet.Cells[1, 5] = "DesvioPadrão Ret.";
            objSheet.get_Range("E1", "E1").EntireColumn.ColumnWidth = 16;
            objSheet.get_Range("E1", "E1").EntireColumn.NumberFormat = "0,000%";

            objSheet.Cells[1, 6] = "VaR Paramétrico";
            objSheet.get_Range("F1", "F1").EntireColumn.ColumnWidth = 18;
            objSheet.get_Range("F1", "F1").EntireColumn.NumberFormat = "R$ #.##0,00_);[Vermelho](R$ #.##0,00)";

            objSheet.Cells[1, 7] = "VaR Histórico";
            objSheet.get_Range("G1", "G1").EntireColumn.ColumnWidth = 18;
            objSheet.get_Range("G1", "G1").EntireColumn.NumberFormat = "R$ #.##0,00_);[Vermelho](R$ #.##0,00)";

            // Preenche os VaRs
            for (int i = 0; i < carteira.vetAcao.Count; i++)
            {
                objSheet.Cells[i + 2, 1] = carteira.vetAcao[i].Nome;
                objSheet.Cells[i + 2, 2] = Convert.ToDouble(carteira.vetAcao[i].Quantidade);
                objSheet.Cells[i + 2, 3] = Convert.ToDouble(carteira.vetAcao[i].Posicao);
                objSheet.Cells[i + 2, 4] = carteira.vetAcao[i].Media;
                objSheet.Cells[i + 2, 5] = carteira.vetAcao[i].DesvioPadrao;
                objSheet.Cells[i + 2, 6] = carteira.vetAcao[i].VarParametrico;
                objSheet.Cells[i + 2, 7] = ((AcaoSimulacao)carteira.vetAcao[i]).VarHistorico;
            }
        }

        private static void imprimirMatrizCovariancia(Worksheet objSheet, Carteira carteira)
        {
            objSheet.Cells[1, 9] = "Matriz de Covariância de Retornos";
            objSheet.get_Range("J1", "AB1").EntireColumn.NumberFormat = "0,000%";
            objSheet.get_Range("I2", "AB21").HorizontalAlignment = XlVAlign.xlVAlignCenter;
            objSheet.get_Range("I1", "I1").EntireColumn.ColumnWidth = 9.57;
            objSheet.get_Range("J2", "AB2").Font.Bold = true;
            objSheet.get_Range("I3", "I21").Font.Bold = true;

            objSheet.get_Range("I1", "L1").BorderAround(XlLineStyle.xlContinuous,
                XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic,
                XlColorIndex.xlColorIndexAutomatic);
            objSheet.get_Range("I2", "AB21").BorderAround(XlLineStyle.xlContinuous,
                XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic,
                XlColorIndex.xlColorIndexAutomatic);

            // Preenche a Matriz de Covariância de Retornos
            for (int i = 0; i < carteira.vetAcao.Count; i++)
            {
                for (int j = 0; j < carteira.vetAcao.Count; j++)
                {
                    objSheet.Cells[j + 3, i + 10] = carteira.matrizCovariancia[i, j];
                }

                objSheet.Cells[2, i + 10] = carteira.vetAcao[i].Nome;
                objSheet.Cells[i + 3, 9] = carteira.vetAcao[i].Nome;
            }
        }

        private static void imprimirMatrizDecomposicaoCholesky(Worksheet objSheet, Carteira carteira)
        {
            objSheet.Cells[23, 9] = "Matriz de Decomposição de Cholesky";
            objSheet.get_Range("I23", "I23").Font.Bold = true;
            objSheet.get_Range("I24", "AB43").HorizontalAlignment = XlVAlign.xlVAlignCenter;
            objSheet.get_Range("J24", "AB24").Font.Bold = true;
            objSheet.get_Range("I25", "I43").Font.Bold = true;
            objSheet.get_Range("I23", "L23").BorderAround(XlLineStyle.xlContinuous,
                XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic,
                XlColorIndex.xlColorIndexAutomatic);
            objSheet.get_Range("I24", "AB43").BorderAround(XlLineStyle.xlContinuous,
                XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic,
                XlColorIndex.xlColorIndexAutomatic);

            // Preenche a matriz de Matriz de Decomposição de Cholesky
            for (int i = 0; i < carteira.vetAcao.Count; i++)
            {
                for (int j = 0; j < carteira.vetAcao.Count; j++)
                {
                    objSheet.Cells[j + 25, i + 10] = ((CarteiraSimulacao)carteira).MatrizDecomposicaoCholesky[i, j];
                }

                objSheet.Cells[24, i + 10] = carteira.vetAcao[i].Nome;
                objSheet.Cells[i + 25, 9] = carteira.vetAcao[i].Nome;
            }
        }

        private static void imprimirMatrizCorrelacao(Worksheet objSheet, Carteira carteira)
        {
            objSheet.Cells[45, 9] = "Matriz de Correlação de Retornos";
            objSheet.get_Range("I45", "I45").Font.Bold = true;
            objSheet.get_Range("I46", "AB65").HorizontalAlignment = XlVAlign.xlVAlignCenter;
            objSheet.get_Range("J46", "AB46").Font.Bold = true;
            objSheet.get_Range("I47", "I65").Font.Bold = true;

            objSheet.get_Range("I45", "L45").BorderAround(XlLineStyle.xlContinuous,
                XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic,
                XlColorIndex.xlColorIndexAutomatic);
            objSheet.get_Range("I46", "AB65").BorderAround(XlLineStyle.xlContinuous,
                XlBorderWeight.xlMedium, XlColorIndex.xlColorIndexAutomatic,
                XlColorIndex.xlColorIndexAutomatic);

            // Preenche a Matriz de Correlação de Retornos
            for (int i = 0; i < carteira.vetAcao.Count; i++)
            {
                for (int j = 0; j < carteira.vetAcao.Count; j++)
                {
                    objSheet.Cells[j + 47, i + 10] = carteira.matrizCorrelacao[i, j];
                }

                objSheet.Cells[46, i + 10] = carteira.vetAcao[i].Nome;
                objSheet.Cells[i + 47, 9] = carteira.vetAcao[i].Nome;
            }
        }

        private static void imprimirDadosAcoes(Worksheet objSheet, Carteira carteira)
        {
            objSheet.Cells[1, 30] = "Ação";
            objSheet.Cells[1, 31] = "Dia";
            objSheet.Cells[1, 32] = "Valor";
            objSheet.Cells[1, 33] = "Retorno Geométrico";
            objSheet.Cells[1, 34] = "Retorno Simulado";
            objSheet.Cells[1, 35] = "Perda ou Ganho Histórico";
            objSheet.Cells[45, 9] = "Matriz de Correlação de Retornos";
            
            objSheet.get_Range("AD1", "AI1").EntireColumn.HorizontalAlignment = XlVAlign.xlVAlignCenter;
            objSheet.get_Range("AD1", "AI1").Font.Bold = true;
            objSheet.get_Range("AD1", "AD1").EntireColumn.Font.Bold = true;

            objSheet.get_Range("AF1", "AF1").EntireColumn.NumberFormat = "R$ #.##0,00_);[Vermelho](R$ #.##0,00)";
            objSheet.get_Range("AG1", "AH1").EntireColumn.NumberFormat = "0,000%";
            objSheet.get_Range("AI1", "AI1").EntireColumn.NumberFormat = "R$ #.##0,00_);[Vermelho](R$ #.##0,00)";

            objSheet.get_Range("AD1", "AD1").EntireColumn.ColumnWidth = 9.57;
            objSheet.get_Range("AE1", "AE1").EntireColumn.ColumnWidth = 10;
            objSheet.get_Range("AF1", "AF1").EntireColumn.ColumnWidth = 9.57;
            objSheet.get_Range("AG1", "AG1").EntireColumn.ColumnWidth = 18;
            objSheet.get_Range("AH1", "AH1").EntireColumn.ColumnWidth = 18;
            objSheet.get_Range("AI1", "AI1").EntireColumn.ColumnWidth = 23;

            // Preenche os retornos
            for (int i = 0; i < carteira.vetAcao.Count; i++)
            {
                for (int j = 0; j < carteira.vetAcao[i].VetDados.Count; j++)
                {
                    objSheet.Cells[(i * carteira.vetAcao[i].VetDados.Count) + j + 2, 30] = carteira.vetAcao[i].Nome;
                    objSheet.Cells[(i * carteira.vetAcao[i].VetDados.Count) + j + 2, 31] = carteira.vetAcao[i].VetDados[j].Dia.ToString("dd/MM/yy");
                    objSheet.Cells[(i * carteira.vetAcao[i].VetDados.Count) + j + 2, 32] = carteira.vetAcao[i].VetDados[j].Valor;
                    objSheet.Cells[(i * carteira.vetAcao[i].VetDados.Count) + j + 2, 33] = carteira.vetAcao[i].VetDados[j].RetornoGeometrico;
                    objSheet.Cells[(i * carteira.vetAcao[i].VetDados.Count) + j + 2, 34] = ((DadoSimulacao)carteira.vetAcao[i].VetDados[j]).RetornoSimulado;
                    objSheet.Cells[(i * carteira.vetAcao[i].VetDados.Count) + j + 2, 35] = ((DadoSimulacao)carteira.vetAcao[i].VetDados[j]).PerdaOuGanhoHistorico;
                }
            }
        }
    }
}